// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using SnapObjects.Commons;

namespace SnapObjects.Data
{
    internal class JsonTemplateImportDataParser<TModel> : ITemplateImportDataParser
    {
        private TemplateImportSchema _schema;

        private ModelEntry<TModel> _entry;

        public IEnumerable<IModelEntry> Parse(string data, TemplateImportSchema schema)
        {
            _schema = schema;

            _entry = new ModelEntry<TModel>(ModelState.NewModified);

            var reader = JNodeReader.Parse(data);

            var entries = this.Parse(reader);

            return entries;
        }

        private List<ModelEntry<TModel>> Parse(INodeReader reader, TemplateImportDataNode parent = null)
        {
            var entries = new List<ModelEntry<TModel>>();

            var node = new TemplateImportDataNode
            {
                NodeName = reader.Name,
                NodeType = reader.NodeType,
                Parent = parent
            };

            while (reader.Read())
            {
                if (reader.CurrentNodeType == DataNodeType.Value)
                {
                    var objItem = reader.ValueReader;

                    if (!_schema.Nodes.ContainsKey(objItem.Name))
                    {
                        continue;
                    }

                    var column = _schema.Nodes[objItem.Name];

                    object value;

                    try
                    {
                        value = Utils.ChangeType(objItem.Value, column.ColumnType);
                    }
                    catch
                    {
                        throw new Exception(String.Format(
                            Properties.Resource.TEMPLATEEXPORT_VALUECONVERT_EXCEPTION,
                            objItem.Value,
                            column.ColumnName,
                            column.ColumnType));
                    }

                    if (entries.Count == 0)
                    {
                        _entry.SetPropertyEntry(column.ColumnName, value, value, PropertyState.Modified);
                    }
                    else
                    {
                        foreach (var item in entries)
                        {
                            item.SetPropertyEntry
                            (
                                column.ColumnName, value, value, PropertyState.Modified
                            );
                        }
                    }

                    var nodeItem = new TemplateImportDataNodeItem
                    {
                        NodeName = column.ColumnName,
                        NodeValue = value,
                        NodeType = DataNodeType.Value
                    };

                    node.Childs.Add(nodeItem);
                }
                else
                {
                    var r = reader.ReadAsNode();

                    var result = this.Parse(r, node);

                    entries.AddRange(result);

                    if (node.NodeType == DataNodeType.Array && result.Count == 0 && _entry.Properties.Length != 0)
                    {
                        entries.Add(_entry);

                        _entry = new ModelEntry<TModel>(ModelState.NewModified);

                        this.SetGroupItem(node);
                    }
                }
            }

            return entries;
        }

        private void SetGroupItem(TemplateImportDataNode node)
        {
            do
            {
                foreach (var item in node.Childs)
                {
                    _entry.SetPropertyEntry
                    (
                        item.NodeName, item.NodeValue, item.NodeValue, PropertyState.Modified
                    );
                }

                node = node.Parent;

            } while (node != null);
        }
    }
}
